home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / jade / src / editrect.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  9KB  |  340 lines

  1. /* editrect.c -- Manipulating `rectangles' of text
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4.    This file is part of Jade.
  5.  
  6.    Jade is free software; you can redistribute it and/or modify it
  7.    under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    Jade is distributed in the hope that it will be useful, but
  12.    WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with Jade; see the file COPYING.    If not, write to
  18.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22.  
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <ctype.h>
  26.  
  27. #ifdef NEED_MEMORY_H
  28. # include <memory.h>
  29. #endif
  30.  
  31. _PR bool rect_insert(TX *, const u_char *, long, POS *);
  32. _PR bool rect_delete(TX *, POS *, POS *);
  33. _PR void order_rect(TX *, POS *, POS *, POS *, POS *);
  34. _PR VALUE rect_copy(TX *, POS *, POS *);
  35. _PR void editrect_init(void);
  36.  
  37. bool
  38. rect_insert(TX *tx, const u_char *text, long textLen, POS *pos)
  39. {
  40.     long len, col = pos->pos_Col;
  41.     LINE *line = tx->tx_Lines + pos->pos_Line;
  42.     POS orig;
  43.     while(textLen > 0 && pos->pos_Line < tx->tx_NumLines)
  44.     {
  45.     const u_char *end = memchr(text, '\n', textLen);
  46.     if(!end)
  47.     {
  48.         end = text + textLen;
  49.         len = textLen;
  50.         textLen = 0;
  51.     }
  52.     else
  53.     {
  54.         len = end - text;
  55.         textLen -= len + 1;
  56.     }
  57.     if(!pad_pos(tx, pos))
  58.         goto nomem;
  59.     orig = *pos;
  60.     if(!insert_str_n(tx, text, len, pos))
  61.         goto nomem;
  62.     undo_record_insertion(tx, &orig, pos);
  63.     flag_insertion(tx, &orig, pos);
  64.     pos->pos_Line++;
  65.     pos->pos_Col = col;
  66.     line++;
  67.     text = (*end == '\n') ? end + 1 : end;
  68.     }
  69.     if(textLen > 0 && pos->pos_Line == tx->tx_NumLines)
  70.     {
  71.     /*
  72.      * Now start expanding off the bottom of the file.
  73.      */
  74.     orig = *pos;
  75.     while(textLen > 0)
  76.     {
  77.         const u_char *end = memchr(text, '\n', textLen);
  78.         u_char *new;
  79.         if(!end)
  80.         {
  81.         end = text + textLen;
  82.         len = textLen;
  83.         textLen = 0;
  84.         }
  85.         else
  86.         {
  87.         len = end - text;
  88.         textLen -= len + 1;
  89.         }
  90.         if(!resize_line_list(tx, +1, pos->pos_Line))
  91.         goto nomem;
  92.         if(!(new = str_alloc(col + len + 1)))
  93.         goto nomem;
  94.         memset(new, ' ', col);
  95.         memcpy(new + col, text, len);
  96.         new[col + len] = 0;
  97.         tx->tx_Lines[pos->pos_Line].ln_Strlen = col + len + 1;
  98.         tx->tx_Lines[pos->pos_Line].ln_Line = new;
  99.         adjust_marks_add_y(tx, +1, pos->pos_Line);
  100.         pos->pos_Line++;
  101.         line++;
  102.         text = (*end == '\n') ? end + 1 : end;
  103.     }
  104.     if(len > 0)
  105.         pos->pos_Col += len;
  106.     pos->pos_Line--;
  107.     undo_record_insertion(tx, &orig, pos);
  108.     flag_insertion(tx, &orig, pos);
  109.     }
  110.     return(TRUE);
  111. nomem:
  112.     mem_error();
  113.     return(FALSE);
  114. }
  115.  
  116. bool
  117. rect_delete(TX *tx, POS *startPos, POS *endPos)
  118. {
  119.     LINE *line = tx->tx_Lines + startPos->pos_Line;
  120.     while(startPos->pos_Line <= endPos->pos_Line)
  121.     {
  122.     POS start;
  123.     start.pos_Col = char_col(tx, startPos->pos_Col, startPos->pos_Line);
  124.     if(line->ln_Strlen > start.pos_Col)
  125.     {
  126.         long del_len, spc_len;
  127.         POS end;
  128.         long start_glyph = glyph_col(tx, start.pos_Col,
  129.                      startPos->pos_Line);
  130.         if((start_glyph != startPos->pos_Col) && (start.pos_Col > 0))
  131.         {
  132.         start.pos_Col--;
  133.         start_glyph = glyph_col(tx, start.pos_Col, startPos->pos_Line);
  134.         }
  135.         end.pos_Col = char_col(tx, endPos->pos_Col, startPos->pos_Line);
  136.         if(end.pos_Col >= line->ln_Strlen)
  137.         end.pos_Col = line->ln_Strlen - 1;
  138.         del_len = end.pos_Col - start.pos_Col;
  139.         spc_len = startPos->pos_Col - start_glyph;
  140.         start.pos_Line = end.pos_Line = startPos->pos_Line;
  141.         undo_record_deletion(tx, &start, &end);
  142.         if(!delete_chars(tx, &start, del_len))
  143.         return(FALSE);
  144.         flag_deletion(tx, &start, &end);
  145.  
  146.         /* May need to insert some spaces to compensate for a deleted
  147.            TAB which crossed the start of the deletion.  */
  148.         if(spc_len > 0)
  149.         {
  150.         if(!insert_gap(tx, spc_len, &start))
  151.             return(FALSE);
  152.         memset(line->ln_Line + start.pos_Col, ' ', spc_len);
  153.         end.pos_Col = start.pos_Col + spc_len;
  154.         undo_record_insertion(tx, &start, &end);
  155.         flag_insertion(tx, &start, &end);
  156.         }
  157.     }
  158.     startPos->pos_Line++;
  159.     line++;
  160.     }
  161.     return(TRUE);
  162. }
  163.  
  164. /* Makes sure that the corners of a rectangle are how I want them. Converts
  165.    from character positions to glyph positions.  */
  166. void
  167. order_rect(TX *tx, POS *dstart, POS *dend, POS *sstart, POS *send)
  168. {
  169.     long start_col = glyph_col(tx, sstart->pos_Col, sstart->pos_Line);
  170.     long end_col = glyph_col(tx, send->pos_Col, send->pos_Line);
  171.     if(start_col < end_col)
  172.     {
  173.     dstart->pos_Col = start_col;
  174.     dend->pos_Col = end_col;
  175.     }
  176.     else
  177.     {
  178.     dstart->pos_Col = end_col;
  179.     dend->pos_Col = start_col;
  180.     }
  181.     if(sstart->pos_Line < send->pos_Line)
  182.     {
  183.     dstart->pos_Line = sstart->pos_Line;
  184.     dend->pos_Line = send->pos_Line;
  185.     }
  186.     else
  187.     {
  188.     dstart->pos_Line = send->pos_Line;
  189.     dend->pos_Line = sstart->pos_Line;
  190.     }
  191. }
  192.  
  193. /* Copies a rectangle to a string, newlines are inserted between lines. */
  194. VALUE
  195. rect_copy(TX *tx, POS *startPos, POS *endPos)
  196. {
  197.     VALUE res;
  198.     u_char *buf = str_alloc(128);
  199.     long len = 0, buflen = 128;
  200.     long linenum = startPos->pos_Line;
  201.     LINE *line = tx->tx_Lines + linenum;
  202.     long width = (endPos->pos_Col - startPos->pos_Col) + 1;
  203.     if(!buf)
  204.     return(mem_error());
  205.     while(linenum <= endPos->pos_Line)
  206.     {
  207.     long glyphs;
  208.     if(len + width >= buflen)
  209.     {
  210.         long newlen = buflen * 2;
  211.         u_char *new = str_alloc(newlen);
  212.         if(!new)
  213.         {
  214.         str_free(buf);
  215.         return(mem_error());
  216.         }
  217.         memcpy(new, buf, len);
  218.         str_free(buf);
  219.         buf = new;
  220.         buflen = newlen;
  221.     }
  222.     len += expand_tabs(tx, buf + len, startPos->pos_Col, endPos->pos_Col,
  223.                linenum, &glyphs);
  224.     if(glyphs < width - 1)
  225.     {
  226.         glyphs = width - glyphs - 1;
  227.         memset(buf + len, ' ', glyphs);
  228.         len += glyphs;
  229.     }
  230.     buf[len++] = '\n';
  231.     linenum++;
  232.     line++;
  233.     }
  234.     res = string_dupn(buf, len);
  235.     str_free(buf);
  236.     return(res);
  237. }
  238.  
  239. _PR VALUE cmd_insert_rect(VALUE str, VALUE pos, VALUE buff);
  240. DEFUN_INT("insert-rect", cmd_insert_rect, subr_insert_rect, (VALUE str, VALUE lpos, VALUE buff), V_Subr3, DOC_insert_rect, "sString to insert rectangularly") /*
  241. ::doc:insert_rect::
  242. insert-rect STRING [POS] [BUFFER]
  243.  
  244. Inserts STRING into BUFFER at POS treating it as a ``rectangle'' of
  245. text -- that is, each separate line in STRING (separated by newlines) is
  246. inserted at the *same* column in successive lines.
  247. ::end:: */
  248. {
  249.     POS pos;
  250.     DECLARE1(str, STRINGP);
  251.     if(POSP(lpos))
  252.     pos = VPOS(lpos);
  253.     else
  254.     pos = curr_vw->vw_CursorPos;
  255.     if(!BUFFERP(buff))
  256.     buff = VAL(curr_vw->vw_Tx);
  257.     if(!read_only(VTX(buff)) && pad_pos(VTX(buff), &pos))
  258.     {
  259.     if(rect_insert(VTX(buff), VSTR(str), STRING_LEN(str), &pos))
  260.         return(sym_t);
  261.     }
  262.     return(sym_nil);
  263. }
  264.  
  265. _PR VALUE cmd_delete_rect(VALUE lstart, VALUE lend, VALUE buff);
  266. DEFUN("delete-rect", cmd_delete_rect, subr_delete_rect, (VALUE lstart, VALUE lend, VALUE buff), V_Subr3, DOC_delete_rect) /*
  267. ::doc:delete_rect::
  268. delete-rect START-POS END-POS [BUFFER]
  269.  
  270. Deletes the rectangle of text from one corner, START-POS, to the opposite
  271. corner, END-POS.
  272. ::end:: */
  273. {
  274.     POS start, end;
  275.     DECLARE1(lstart, POSP);
  276.     DECLARE2(lend, POSP);
  277.     if(!BUFFERP(buff))
  278.     buff = VAL(curr_vw->vw_Tx);
  279.     order_rect(VTX(buff), &start, &end, &VPOS(lstart), &VPOS(lend));
  280.     if(!read_only(VTX(buff)) && check_line(VTX(buff), &end))
  281.     {
  282.     rect_delete(VTX(buff), &start, &end);
  283.     return(sym_t);
  284.     }
  285.     return(sym_nil);
  286. }
  287.  
  288. _PR VALUE cmd_copy_rect(VALUE lstart, VALUE lend, VALUE buff);
  289. DEFUN("copy-rect", cmd_copy_rect, subr_copy_rect, (VALUE lstart, VALUE lend, VALUE buff), V_Subr3, DOC_copy_rect) /*
  290. ::doc:copy_rect::
  291. copy-rect START-POS END-POS [BUFFER]
  292.  
  293. Returns the rectangle of text marked out by START-POS and END-POS.
  294. ::end:: */
  295. {
  296.     POS start, end;
  297.     DECLARE1(lstart, POSP);
  298.     DECLARE2(lend, POSP);
  299.     if(!BUFFERP(buff))
  300.     buff = VAL(curr_vw->vw_Tx);
  301.     order_rect(VTX(buff), &start, &end, &VPOS(lstart), &VPOS(lend));
  302.     if(check_line(VTX(buff), &end))
  303.     return(rect_copy(VTX(buff), &start, &end));
  304.     return(sym_nil);
  305. }
  306.  
  307. _PR VALUE cmd_cut_rect(VALUE lstart, VALUE lend, VALUE buff);
  308. DEFUN("cut-rect", cmd_cut_rect, subr_cut_rect, (VALUE lstart, VALUE lend, VALUE buff), V_Subr3, DOC_cut_rect) /*
  309. ::doc:cut_rect::
  310. cut-rect START-POS END-POS [BUFFER]
  311.  
  312. The same as `copy-rect' except that the section of text copied (START-POS
  313. to END-POS) is deleted from the file after being duplicated.
  314. ::end:: */
  315. {
  316.     POS start, end;
  317.     DECLARE1(lstart, POSP);
  318.     DECLARE2(lend, POSP);
  319.     if(!BUFFERP(buff))
  320.     buff = VAL(curr_vw->vw_Tx);
  321.     order_rect(VTX(buff), &start, &end, &VPOS(lstart), &VPOS(lend));
  322.     if(!read_only(VTX(buff)) && check_line(VTX(buff), &end))
  323.     {
  324.     VALUE str = rect_copy(VTX(buff), &start, &end);
  325.     if(str)
  326.         rect_delete(VTX(buff), &start, &end);
  327.     return(str);
  328.     }
  329.     return(sym_nil);
  330. }
  331.  
  332. void
  333. editrect_init(void)
  334. {
  335.     ADD_SUBR(subr_insert_rect);
  336.     ADD_SUBR(subr_delete_rect);
  337.     ADD_SUBR(subr_copy_rect);
  338.     ADD_SUBR(subr_cut_rect);
  339. }
  340.